package pns.polaris.filescan.init;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import pns.polaris.filescan.process.AbsFileScanLoad;
import psn.polaris.init.AbsPolarisOwnerStartProcess;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

@Component
public class FileScanStartProcess extends AbsPolarisOwnerStartProcess {

    @Autowired
    private List<AbsFileScanLoad> FILE_SCAN_LOAD_LIST = new ArrayList<>();

    public FileScanStartProcess() {
        super(Integer.MIN_VALUE+1);
    }

    /**
     * 初始化执行方法
     */
    @Override
    public void processInit() {
        logger.info("文件启动扫描开始");
        Set<String> paths = new HashSet<>();
        // 扫描Jar文件
        findApplicationJar(paths);
        // 解析Jar文件
        for (String path : paths) {
            analysisJarFile(path);
        }
        // 扫描classpath路径
        paths.clear();
        findApplicationClassPath(paths);
        // 解析classpath文件
        for (String path : paths) {
            fileScan(new File(path));
        }
    }

    protected void findApplicationJar(Set<String> paths) {
        logger.info("查找Web工程下的jar文件列表开始...");
        Enumeration<URL> urls = null;
        try {
            urls = Thread.currentThread().getContextClassLoader().getResources("META-INF/MANIFEST.MF");
        } catch (IOException e) {
            logger.error("获取 MANIFEST.MF 发生错误，扫描终止");
        }
        if(null!=urls){
            while (urls.hasMoreElements()){
                URL url = urls.nextElement();
                String path = url.toString();
                // Weblogic容器中存在这种情况
                path = path.replaceAll("/\\./", "/");
                paths.addAll(dealJarSource(path));
            }
        }
        logger.info("查找Web工程下的jar文件列表完成");
    }

    /**
     * 解析获取Jar包路径
     * @param path  META-INF/MANIFEST.MF 路径
     * @return  路径列表
     */
    @SuppressWarnings("AlibabaUndefineMagicConstant")
    public List<String> dealJarSource(String path) {
        List<String> list = new ArrayList<String>();
        if (!path.contains("!")) {
            // 如果不存在!则说明不是jar文件，此时直接通过:截取
            path = path.substring(0, path.length() - "META-INF/MANIFEST.MF".length() - 1);
            path = path.substring(path.indexOf(':') + 1);
            logger.debug("发现Jar路径：{}",path);
            list.add(path);
        }
        // 用!截取，如果截取为两段则说明是一个jar包中存在当前配置文件，超过两段则说明是jar包中嵌套了jar包
        String[] pathArray = path.split("!");
        if (pathArray.length <= 2) {
            logger.debug("发现Jar路径：{}",pathArray[0]);
            list.add(pathArray[0]);
        }else {
            logger.warn("存在jar包嵌套jar包的路径：{}，Gadgets暂不支持此场景", path);
        }
        return list;
    }

    /**
     * 查找classPath地址
     * @param paths 路径容器
     */
    protected void findApplicationClassPath(Set<String> paths) {
        logger.info("查找classes路径开始...");
        URL url = Objects.requireNonNull(ClassUtils.getDefaultClassLoader()).getResource("/");
        if (null == url) {
            url = Objects.requireNonNull(ClassUtils.getDefaultClassLoader()).getResource("");
        }
        if(null != url){
            logger.debug("发现classes路径：{}",url.getPath());
            paths.add(url.getPath());
        }
        String classPathProperty = System.getProperty("java.class.path");
        String operateSys = System.getProperty("os.name").toLowerCase();
        String[] classPaths = null;
        if (operateSys.contains("windows")) {
            classPaths = classPathProperty.split(";");
        } else {
            classPaths = classPathProperty.split(":");
        }
        for (String classPath : classPaths) {
            logger.debug("发现classes路径：{}",classPath);
            paths.add(classPath);
        }
        logger.info("查找classes路径完成");
    }

    protected void analysisJarFile(String path) {
        JarFile jarFile = null;
        try {
            jarFile = new JarFile(path);
        } catch (IOException e) {
            logger.error("解析Jar文件{}失败",path);
        }
        if (null != jarFile) {
            // 如果创建 JarFile 成功，则解析Jar包文件内的内容
            Enumeration<JarEntry> enumeration = jarFile.entries();
            while (enumeration.hasMoreElements()) {
                JarEntry entry = enumeration.nextElement();
                logger.debug("当前文件名：{}", entry.getName());
                for (AbsFileScanLoad abstractFileScanLoad : FILE_SCAN_LOAD_LIST) {
                    if (abstractFileScanLoad.fileJudge(entry.getName())) {
                        logger.info("发现文件：{}",entry.getName());
                        abstractFileScanLoad.analysisFile(jarFile, entry);
                    }
                }
            }
        }
    }

    /**
     * 文件扫描
     * @param rootFile  根文件
     */
    protected void fileScan(File rootFile){
        if(rootFile.isFile()){
            for (AbsFileScanLoad abstractFileScanLoad : FILE_SCAN_LOAD_LIST) {
                logger.debug("扫描到文件：{}",rootFile.getPath());
                if(abstractFileScanLoad.fileJudge(rootFile.getName())){
                    logger.info("发现文件：{}",rootFile.getPath());
                    abstractFileScanLoad.analysisFile(rootFile);
                }
            }
        }else {
            File[] sonFiles = rootFile.listFiles();
            if(null!=sonFiles && sonFiles.length>0){
                for (File sonFile : sonFiles) {
                    fileScan(sonFile);
                }
            }
        }
    }
}
